// Filename: mm.rs

/*
 * Copyright (c) 2020 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU)
 * OS-Lab-2020 (i.e., ChCore) is licensed under the Mulan PSL v1.
 * You can use this software according to the terms and conditions of the Mulan PSL v1.
 * You may obtain a copy of Mulan PSL v1 at:
 *   http://license.coscl.org.cn/MulanPSL
 *   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 *   PURPOSE.
 *   See the Mulan PSL v1 for more details.
 */
// SPDX-License-Identifier: BSD 3-Clause
// Copyright (c) 2024 thinkerhui <thinkerhui@qq.com>
// Copyright (c) 2024 McDj26 <2645370670@qq.com>
// Copyright (c) 2024 qiyf6 <1076753979@qq.com>
use crate::{
    common::{
        list::ListHead,
        mmu::{phys_to_virt, KERNEL_PT},
        synchronization::NullLock,
        types::{paddr_t, size_t, vaddr_t},
        vars::KBASE,
    },
    kinfo,
    mm::page_table::map_range_in_pgtbl,
    mm::{
        buddy::{init_buddy, FreeList, Page, PhysMemPool},
        slab::init_slab,
    },
    qemu_println, BUG, ROUND_UP,
};
// use std::{format, default, ptr::null};
pub const PAGE_SIZE: u64 = 0x1000;
const PHYSICAL_MEM_START: u64 = 24 * 1024 * 1024;
use lazy_static::lazy_static;
lazy_static! {
    static ref START_VADDR: u64 = phys_to_virt(PHYSICAL_MEM_START);
    // static mut global_mem: PhysMemPool = Mutex::new(PhysMemPool::Default::default());
}
pub static mut global_mem: PhysMemPool = PhysMemPool {
    inner: NullLock::<PhysMemPoolInner>::new(PhysMemPoolInner {
        pool_start_addr: 0,
        pool_mem_size: 0,
        pool_phys_page_num: 0,
        page_metadata: core::ptr::null_mut(),
        free_lists: [
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 0,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 1,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 2,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 3,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 4,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 5,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 6,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 7,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 8,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 9,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 10,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 11,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 12,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
            FreeList {
                free_list: ListHead {
                    next: core::ptr::null_mut(),
                    prev: core::ptr::null_mut(),
                    data: Page {
                        allocated: false,
                        order: 13,
                        node: core::ptr::null_mut(),
                        slab: core::ptr::null_mut(),
                    },
                },
                nr_free: 0,
            },
        ],
    }),
};

const NPAGES: u64 = 128 * 1000;
const PHYSICAL_MEM_END: u64 = PHYSICAL_MEM_START + NPAGES * PAGE_SIZE;
// pub static mut global_mem: PhysMemPool = Default::default();
// use std::arch::asm;
use core::arch::asm;

use super::buddy::PhysMemPoolInner;
extern "C" {
    pub static img_end: u64;
}

fn get_ttbr1() -> u64 {
    let mut pgd: u64 = 0;
    unsafe {
        // 下面这个东西ChatGPT3.5写了半天没写对，还是自己看一下文档一下子就写出来了
        asm!("mrs {}, ttbr1_el1", out(reg) pgd);
    }
    pgd
}

fn map_kernel_space(va: vaddr_t, pa: paddr_t, len: size_t) {
    let ttbr1 = get_ttbr1() as *mut vaddr_t;
    map_range_in_pgtbl(ttbr1, va, pa, len, KERNEL_PT);
}

fn kernel_space_check() {
    unsafe {
        for i in 128..256 {
            let kernel_val = *((KBASE + (i << 21)) as *const u64);
            kinfo!("kernel_val: {:#x}", kernel_val);
        }
    }
    kinfo!("kernel space check pass\n");
}

pub fn set_page_table(pgtbl: paddr_t) {
    // Implementation for set_page_table in Rust
}

pub fn is_user_addr(vaddr: vaddr_t) -> bool {
    vaddr < KBASE
}

pub fn is_user_addr_range(vaddr: vaddr_t, len: usize) -> bool {
    (vaddr + len as u64 >= vaddr) && is_user_addr(vaddr + len as u64)
}

pub fn mm_init() {
    let mut free_mem_start: u64 = 0;
    let mut page_meta_start: *mut ListHead<Page> = core::ptr::null_mut();
    let mut npages: u64 = 0;
    let mut start_vaddr: u64 = 0;
    // img_end是从linker-aarch64.lds.in汇编引用的,涉及到boot
    unsafe {
        free_mem_start = phys_to_virt(ROUND_UP!((&img_end) as *const _ as u64, PAGE_SIZE));
    }
    npages = NPAGES;
    start_vaddr = *START_VADDR;
    qemu_println!("get global variant succeed!");
    kinfo!(
        "[CHCORE] mm: free_mem_start is 0x{:x}, free_mem_end is 0x{:x}\n",
        free_mem_start,
        phys_to_virt(PHYSICAL_MEM_END)
    );

    if free_mem_start + npages * core::mem::size_of::<Page>() as u64 > start_vaddr {
        BUG!("kernel panic: init_mm metadata is too large!\n");
    }
    qemu_println!("get range of free memory succeed!");
    page_meta_start = free_mem_start as *mut ListHead<Page>;
    kinfo!(
        "page_meta_start: 0x{:x}, real_start_vadd: 0x{:x}, npages: 0x{:x}, meta_page_size: 0x{:x}\n",
        page_meta_start as u64,
        start_vaddr,
        npages,
        core::mem::size_of::<Page>() as u64
    );

    unsafe {
        init_buddy(&mut global_mem, page_meta_start, start_vaddr, npages);
    }
    qemu_println!("buddy system init succeed!");
    // slab allocator for allocating small memory regions
    init_slab();
    qemu_println!("slab system init succeed!");
    map_kernel_space(KBASE + (128u64 << 21), 128u64 << 21, 128u64 << 21);
    // check whether kernel space [KABSE + 256 : KBASE + 512] is mapped
    kernel_space_check();
}
